from pycocotools.coco import COCO
import os
import shutil
from tqdm import tqdm
# import skimage.io as io
import matplotlib.pyplot as plt
import cv2
from PIL import Image, ImageDraw
savepath = r"/home/xys/datasets/Vehicle/coco2017/"# 提取出的类别的保存路径
img_dir = savepath + 'images/'
anno_dir = savepath + 'xml/'
datasets_list = ['train2017','val2017']#需要提取的数据集路径
classes_names_path = '/home/xys/datasets/Vehicle/classes.txt'# 这里填写需要提取的类别；以改动
dataDir = r'/home/xys/datasets/coco2017/coco2017/'# 原coco数据集的目录


def read_class_name(path):        #读取path下的类别民
    f = open(path,'r')
    classes_name = []
    for i in f.readlines():
        classes_name.append(i.strip())
    return classes_name
classes_names = read_class_name(classes_names_path)
headstr = """\
<annotation>
    <folder>VOC</folder>
    <filename>%s</filename>
    <source>
        <database>My Database</database>
        <annotation>COCO</annotation>
        <image>flickr</image>
        <flickrid>NULL</flickrid>
    </source>
    <owner>
        <flickrid>NULL</flickrid>
        <name>company</name>
    </owner>
    <size>
        <width>%d</width>
        <height>%d</height>
        <depth>%d</depth>
    </size>
    <segmented>0</segmented>
"""
objstr = """\
    <object>
        <name>%s</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>%d</xmin>
            <ymin>%d</ymin>
            <xmax>%d</xmax>
            <ymax>%d</ymax>
        </bndbox>
    </object>
"""

tailstr = '''\
</annotation>
'''


def mkr(path):
    '''
    如果path存在，就先删除所有内容再创建，否则直接创建
    '''
    if os.path.exists(path):#先删除再创建
        shutil.rmtree(path)#递归删除目录树
        os.mkdir(path)#创建目录
    else:
        os.mkdir(path)#如果不存在则直接创建

mkr(img_dir)
mkr(anno_dir)


def id2name(coco):
    '''
    读取coco中的所有类，存储在新的classes字典里，以['id':'name']保存
    返回这个新的字典classes
    '''
    classes = dict()#创建一个新的字典
    for cls in coco.dataset['categories']:
        classes[cls['id']] = cls['name']
    return classes


def write_xml(anno_path, head, objs, tail):
    '''
    写入xml文件
    anno_path:路径
    head:头部
    objs:
    tail:尾部
    '''
    f = open(anno_path, "w")#以w打开文件流
    f.write(head)#写入头部
    for obj in objs:#将该objs所有内容写入
        f.write(objstr % (obj[0], obj[1], obj[2], obj[3], obj[4]))
    f.write(tail)#写入尾部


def save_annotations_and_imgs(dataset, filename, objs):
    '''
    保存数据集和图片
    dataset:验证集或者测试集，来自datasets_list
    filename:文件名,比如"COCO_train2014_000000156610.jpg"
    objs:[[类别名，boundingbox坐标],[类别名，boundingbox坐标],......]
    '''
    #当前的图片的annotatons目录和image目录
    anno_path = anno_dir + filename[:-3] + 'xml'#倒数取到COCO_train2014_000000196610.
    img_path = dataDir + dataset + '/' + filename
    dst_imgpath = img_dir + filename#要保存的jpg目录

    img = cv2.imread(img_path)#打开/读取图片
    shutil.copy(img_path, dst_imgpath)#复制文件到另一个地方，返回文件distination

    head = headstr % (filename, img.shape[1], img.shape[0], img.shape[2])
    tail = tailstr
    write_xml(anno_path, head, objs, tail)


def showimg(coco, dataset, img, classes, cls_id, show=True):
    '''
    coco:coco对象
    dataset:验证集或者测试集，来自datasets_list
    img:一个img字典，包含该图片的所有信息
    classes:COCO中所有的类，80个，列表
    cls_id:我们想要检测的类别classes_names[]对应的id
    show:是否显示
    return:[[类别名，boundingbox坐标],[类别名，boundingbox坐标],......]
    '''
    global dataDir#将dataDir声明为一个全局变量，在此局部或全局范围内均可访问或者修改
    I = Image.open('%s/%s/%s' % (dataDir, dataset, img['file_name']))#打开一张图片，参数为图片路径
    annIds = coco.getAnnIds(imgIds=img['id'], catIds=cls_id, iscrowd=None)    # 通过id，得到注释的信息
    anns = coco.loadAnns(annIds)#得到一个list，包含该id的4个注释信息
    # coco.showAnns(anns)
    objs = []
    for ann in anns:
        class_name = classes[ann['category_id']]#获取类别名
        if class_name in classes_names:
            if 'bbox' in ann:
                bbox = ann['bbox']
                xmin = int(bbox[0])
                ymin = int(bbox[1])
                xmax = int(bbox[2] + bbox[0])
                ymax = int(bbox[3] + bbox[1])
                obj = [class_name, xmin, ymin, xmax, ymax]#将类别名，boundingbox的坐标存入objs[]
                objs.append(obj)
                draw = ImageDraw.Draw(I)#绘图
                draw.rectangle([xmin, ymin, xmax, ymax])#绘制矩形框
    if show:
        plt.figure()#创建一个新的图
        plt.axis('off')
        plt.imshow(I)#设置显示的对象
        plt.show()#显示
    return objs


for dataset in datasets_list:
    print("加载 {} 文件".format(dataset))
    annFile = '{}annotations/instances_{}.json'.format(dataDir, dataset)

    coco = COCO(annotation_file=annFile)#构建一个微软的COCO类，用来可视化annotation

    classes = id2name(coco)#显示COCO中的所有类
    print("{} 的类别有 {} ".format(dataset,classes))
    classes_ids = coco.getCatIds(catNms=classes_names)#根据列出的classes_names来得到对应的categories的id
    print("只检测的类别 id为{}".format(classes_ids))

    for cls in classes_names:  # 循环对classes_name列表进行处理
        cls_ids = coco.getCatIds(catNms=[cls])
        img_ids = coco.getImgIds(catIds=cls_ids)  # 获取classes_name的图片，满足给定的过滤条件，返回一个列表

        for imgId in tqdm(img_ids,ncols=150,desc="Processing class %s"%cls,postfix=" over "):  # 使用进度条封装len(img_ids)，循环img_ids列表
            img = coco.loadImgs(imgId)[0]  # 得到指定id图片的信息,返回一个字典
            filename = img['file_name']  # 得到该图片的名字
            objs = showimg(coco, dataset, img, classes, classes_ids, show=False)
            save_annotations_and_imgs(dataset, filename, objs)
